package com;

import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.CharEncoding;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.springframework.util.DigestUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.net.ssl.SSLContext;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

public class WxApiUtils {

    private CloseableHttpClient httpClient;
    private boolean certEnabled;

    public WxApiUtils(String key12, String password) throws Exception {
        FileInputStream inputStream = null;
        try {
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            inputStream = new FileInputStream(new File(key12));
            keyStore.load(inputStream, password.toCharArray());
            SSLContext sslcontext = SSLContexts.custom()
                    .loadKeyMaterial(keyStore, password.toCharArray())
                    .build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext);
            httpClient = HttpClients.custom()
                    .setSSLSocketFactory(sslsf)
                    .build();
            certEnabled = true;
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(inputStream);
        }
    }

    /**
     * 不支持证书
     *
     * @throws Exception
     */
    public WxApiUtils() throws Exception {
        httpClient = HttpClients.createDefault();
    }

    public boolean isCertEnabled() {
        return certEnabled;
    }

    public Map<String, String> xml2Map(InputStream inputStream) throws IOException, SAXException, ParserConfigurationException {
        InputSource inputSource = new InputSource(inputStream);
        return xml2Map(inputSource);
    }

    public Map<String, String> xml2Map(String xml) throws IOException, SAXException, ParserConfigurationException {
        InputSource inputSource = new InputSource(new StringReader(xml));
        return xml2Map(inputSource);
    }

    public Map<String, String> xml2Map(InputSource inputSource) throws ParserConfigurationException, IOException, SAXException {
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputSource);
        Element xml = document.getDocumentElement();
        NodeList childNodes = xml.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node item = childNodes.item(i);
            if (item instanceof Element) {
                map.put(item.getNodeName(), item.getTextContent());
            }
        }
        return map;
    }

    /**
     * map 转换 XML字符串； 默认为detail字段CDATA
     *
     * @param map
     * @return
     * @throws IOException
     * @throws ParserConfigurationException
     */
    public String map2Xml(Map<String, String> map) throws IOException, ParserConfigurationException {
        return map2Xml(map, "detail");
    }

    /**
     * map 转换 XML字符串
     *
     * @param map
     * @param cdataColumns CDATA字段列表
     * @return
     * @throws ParserConfigurationException
     * @throws IOException
     */
    public String map2Xml(Map<String, String> map, String... cdataColumns) throws ParserConfigurationException, IOException {
        Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
        Element xml = document.createElement("xml");
        document.appendChild(xml);
        Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            String key = entry.getKey();
            String value = entry.getValue();
            Element element;
            if (StringUtils.endsWithIgnoreCase(key, "!CDATA") || ArrayUtils.contains(cdataColumns, key)) {
                String tagName = StringUtils.substringBeforeLast(key, "!CDATA");
                element = document.createElement(tagName);
                element.appendChild(document.createCDATASection(value));
            } else {
                element = document.createElement(key);
                element.setTextContent(org.apache.commons.lang3.StringUtils.trimToEmpty(value));
            }
            xml.appendChild(element);
        }

        OutputFormat format = new OutputFormat(document);
        format.setIndenting(true);
        format.setOmitXMLDeclaration(true);
        Writer out = new StringWriter();
        XMLSerializer serializer = new XMLSerializer(out, format);
        serializer.serialize(document);
        return out.toString();
    }

    public String post(String url, String body) throws IOException {
        HttpPost httpPost = new HttpPost(url);
        StringEntity entity = new StringEntity(body, CharEncoding.UTF_8);
        entity.setContentType("text/xml;charset=utf-8");
        httpPost.setEntity(entity);
        CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
        String result = EntityUtils.toString(httpResponse.getEntity(), CharEncoding.UTF_8);
        return result;
    }

    public String get(String url, Map<String, String> params) throws IOException {

        return "";
    }

    /**
     * 按微信签名算法生成签名
     *
     * @param paramMap
     * @return
     */
    public String createSign(Map<String, String> paramMap) {
        try {
            StringBuilder builder = new StringBuilder();
            String[] keys = paramMap.keySet().toArray(new String[0]);
            Arrays.sort(keys);//必须排序，这是规定
            for (int i = 0; i < keys.length; i++) {
                String key = keys[i];
                String tagName = StringUtils.substringBeforeLast(key, "!CDATA");
                String value = paramMap.get(key);
                if (value != null) {
                    builder.append(tagName).append("=").append(value).append("&");
                }
            }
            builder.append("key=").append(WeChatConfig.MERCHANT_KEY);
            String md5 = DigestUtils.md5DigestAsHex(builder.toString().getBytes(org.apache.commons.codec.CharEncoding.UTF_8));
            return md5.toUpperCase();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }
}
